
/**
 * promise 特点
 * 1.promise有三个状态  成功态(resolve)  失败态(reject)  等待态(pending)
 * 2.promise 是一个类  可以用来new , promise的参数executor中的内容是立即执行的  但是成功态和失败态是由使用者定义的
 * 3.promise 的实例都拥有一个then方法 then中有两个参数数 第一个是成功的回调  第二个是失败的回调  如果执行函数中出现异常 也会执行失败的回调
 * 4.promise 支持链式调用  它成功的回调和失败的回调的返回值 可以传递到外层的then中  返回的可能是普通值(传递到下一个then的成功中)  可能是promise(取决于promise的状态  决定传递到下一个then中的成功还是失败中)  还有可能是出错的情况(传递到下一个then的失败中，如果下一个then没有处理该错误 则会传递给再下一个then)
 * 5.每次执行完.then之后  都会返回一个"新的promise" (因为如果第一个then失败后，但是在失败回调中返回了一个普通值，那么他会传递给下一个then的成功回调中，这和promise一旦状态改变，就不能再发生变化相悖)
 * 
 * 
 * promise/A+规范  https://promisesaplus.com/    ie不支持  但是polyfill中的es-promise可以为不支持的浏览器抹平差异
 */



// 定义三个状态
const PENDING = 'PENDING'
const SUCCESS = 'SUCCESS'
const REJECTED = 'REJECTED'


const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) return reject(new TypeError('类型错误'))
  let called
  if ((x && typeof x === 'object') || typeof x === 'function') { // 判断 promise
    try {
      let then = x.then
      if (typeof then === 'function') { // 继续判断是否是promise
        then.call(x, y => { // 这里不能再用x.then   有缺陷    then.call 执行这个then 第一个回调是promise x成功的结果 
          if (called) return  // 防止执行了y  还执行r
          called = true
          resolvePromise(promise2, y, resolve, reject) //  y有可能还是promise   递归该方法  直到y的值是普通值
        }, r => {
          if (called) return
          called = true
          reject(r)
        })
      } else {
        if (called) return
        called = true
        resolve(x)
      }
    } catch (error) {
      if (called) return
      called = true
      reject(error)
    }
  } else {
    resolve(x)
  }
}



class Promise {

  constructor(executor) {
    this.status = PENDING  // promise接收一个executor参数 它是立即执行的
    this.value = undefined  // 保存成功的值
    this.reason = undefined  // 保存失败的原因
    this.onSuccessCallbacks = [] // 存放所有成功的回调
    this.onRejectedCallbacks = [] // 存放所有失败的回调
    const resolve = (value) => {
      if (this.status === PENDING) {
        this.value = value
        this.status = SUCCESS
        this.onSuccessCallbacks.forEach(fn => fn()) // 订阅好数组中的事件 等发布的时候  会依次执行
      }
    }

    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason
        this.status = REJECTED
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }
    // 当executor中出错时   也会执行失败的回调
    try {
      executor(resolve, reject)
    } catch (error) {
      reject(error)
    }

  }

  then (onSuccess, onRejected) {
    onSuccess = typeof onSuccess === 'function' ? onSuccess : v => v
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err }
    let promise2 = new Promise((resolve, reject) => { // 每次调用then  都会返回一个新的promise
      if (this.status === SUCCESS) {
        setTimeout(() => {
          try {
            // x有可能是promise  如果是promise的话 应该让promise的拿到它的then结果返回  但是这里传入的promise2是等待当前promose执行完之后 才生成的  所以需要加宏任务延迟执行  
            // 加完宏任务之后  exetutor中try就不生效了 所以需要在这里再加一次try
            let x = onSuccess(this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        });

      }
      if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        });

      }
      // 如果在 new Promise时  里边有异步操作，此时在then执行时  status还是pending状态
      // 所以这里需要用到发布订阅模式  将成功的回调和失败的回调存放起来 稍后调用reslove 和reject的时候  重新执行
      if (this.status === PENDING) {
        // 可以将成功的回调函数名到数组里 但是不利于扩展  可以采用切片的模式  这样可以在其中添加一些额外逻辑
        this.onSuccessCallbacks.push(() => { // 切片模式
          setTimeout(() => {
            try {
              let x = onSuccess(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          });

        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          });

        })
      }
    })
    return promise2
  }
}



// 测试A+规范的库 需要给Promise提供一个deferred方法   需要返回一个对象  包含promise、resolve、reject
Promise.deferred = function () {
  let dfd = {}
  dfd.promise = new Promise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

// 执行 promises-aplus-tests [文件]

module.exports = Promise